package com.mf.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mf.bo.TransactionsBatchBo;
import com.mf.entity.*;
import com.mf.enums.DealTypeEnum;
import com.mf.enums.TransactionStatusEnum;
import com.mf.exceptions.GraceException;
import com.mf.mapper.*;
import com.mf.service.ITransactionsService;
import com.mf.utils.EnumUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 交易表 服务实现类
 * </p>
 *
 * @author mf
 * @since 2024-06-08
 */
@Service
public class TransactionsServiceImpl extends ServiceImpl<TransactionsMapper, Transactions> implements ITransactionsService {
    @Resource
    private TransactionsMapper transactionsMapper;
    @Resource
    private PositionsMapper positionsMapper;
    @Resource
    private CodeMapper codeMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private WorkdayMapper workdayMapper;




    /**
     * 批量添加交易记录 每次交易以1万的本金  价格以基准日期当天的开盘价格为基准价格 交易数量默认为 1万 / 基准价格 取百位的整数 类型只能为买入
     * @param transactionsBatchBo 公共部分
     */
    @Transactional
    @Override
    public void addBatch(TransactionsBatchBo transactionsBatchBo , List<CodeHistory> listHistory) {
        if (!transactionsBatchBo.getType().equals(DealTypeEnum.BUY.status)) {
            GraceException.display("交易类型只能是:" + DealTypeEnum.BUY.status);
            return;
        }

        for (CodeHistory codeHistory : listHistory) {
            Transactions transactions = new Transactions();
            BeanUtil.copyProperties(transactionsBatchBo, transactions);
            transactions.setDm(codeHistory.getDm());
            // 设置交易价格
            transactions.setPrice(codeHistory.getO());

            int quantity = new BigDecimal(10000)
                    .divide(codeHistory.getO(), 0, RoundingMode.HALF_UP) // 四舍五入到小数点后两位
                    .divide(new BigDecimal("100"), 0, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100")) // 乘以100，准备进行整数除法
                    .intValue();
            // 交易数量等于 10000 / 价格 取百位整数
            transactions.setQuantity(quantity);
            add(transactions);
        }

    }

    /**
     * 新增交易记录
     *
     * @param transactions 交易记录
     * @return 添加结果
     */
    @Override
    @Transactional
    public Integer add(Transactions transactions) {
        // 设置交易金额
        transactions.setAmount(transactions.getPrice().multiply(new BigDecimal(transactions.getQuantity())));

        // 计算交易费用
        calculateCost(transactions);
        final User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, transactions.getUserId()));

        if (!isExist(transactions,user)) {
            return null;
        }

        // 获取对应股票持仓数据
        final List<Positions> positionsList =
                positionsMapper.selectList(new LambdaQueryWrapper<Positions>().eq(Positions::getDm, transactions.getDm()));
        // 持仓数量
        final Integer positionsQuantity = getPositions(positionsList);



        // 买入
        if (DealTypeEnum.BUY.status.equals(transactions.getType())) {
            // 买入 没有持仓建仓有加仓
            transactions.setStatus(positionsQuantity == 0 ? TransactionStatusEnum.POTIONS.status :
                    TransactionStatusEnum.ADDITIONAL.status);
            // 复制交易数据添加到持仓表里面 BeanUtils
            final Positions addPositions = new Positions();
            BeanUtil.copyProperties(transactions, addPositions);
            positionsMapper.insert(addPositions);

            // 买入后用户扣除对应的买入金额 和手续费用
            user.setAmount(user.getAmount().subtract(transactions.getAmount()).subtract(transactions.getCost()));


        } else if (DealTypeEnum.SELL.status.equals(transactions.getType())) {
            // 卖出
            if (positionsQuantity == 0) {
                GraceException.display("没有持仓不能卖出");
                return null;
            }

            if (transactions.getQuantity() > positionsQuantity) {
                GraceException.display("持仓数量不足");
                return null;
            }
            boolean isAllQuantity = positionsQuantity.equals(transactions.getQuantity());
            // 卖出 有持仓减仓 没有清仓
            transactions.setStatus(isAllQuantity ? TransactionStatusEnum.REQUEST.status :
                    TransactionStatusEnum.REDUCTION.status);

            // 同步持仓表中数量
            reducePositions(positionsList, transactions, positionsQuantity);

            // 卖出后用户增加对应的卖出金额
            BigDecimal sellAmount = transactions.getPrice().multiply(new BigDecimal(transactions.getQuantity()));

            // 卖出后用户增加对应的卖出金额 (价格 * 数量 - 手续费)
            user.setAmount(user.getAmount().add(sellAmount).subtract(transactions.getCost()));
        }

        // 更新用户现有金额
        userMapper.updateById(user);

        // 增加交易记录
        transactionsMapper.insert(transactions);
        return 1;
    }

    /**
     * 更新持仓数据
     */
    @Transactional
    public void reducePositions(List<Positions> positionsList, Transactions transactions, Integer sumQuantity) {
        // 删除数量等于持仓数量 删除对应代码持仓数据
        if (transactions.getQuantity().equals(sumQuantity)) {
            positionsMapper.delete(new LambdaQueryWrapper<Positions>().eq(Positions::getDm, transactions.getDm()));
        }

        // 深度复制transactions 中数量
        Integer quantity = transactions.getQuantity();

        List<Integer> ids = new ArrayList<>();
        for (Positions positions : positionsList) {
            quantity -= positions.getQuantity();
            // 没有扣减完成 将扣除完的id 用于后续删除对应持仓数据
            if (quantity > 0) {
                ids.add(positions.getId());
            } else if (quantity == 0) {
                // 扣减完成记录id并退出循环
                ids.add(positions.getId());
                break;
            } else {
                positions.setQuantity(Math.abs(quantity));
                final LambdaQueryWrapper<Positions> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(Positions::getId, positions.getId());
                positionsMapper.update(positions, queryWrapper);
            }
        }
        if(!ids.isEmpty()){
            // 删除没有数量的持仓数据
            positionsMapper.delete(new LambdaQueryWrapper<Positions>().in(Positions::getId, ids));
        }

    }

    /**
     * 获取持仓数量
     *
     * @param positionsList 持仓列表
     * @return 持仓数量
     */
    private Integer getPositions(List<Positions> positionsList) {
        int quantity = 0;
        for (Positions positions : positionsList) {
            quantity += positions.getQuantity();
        }
        return quantity;
    }

    /**
     * 计算手续费
     */
    private void calculateCost(Transactions transactions) {
        // 买入
        if (DealTypeEnum.BUY.status.equals(transactions.getType())) {
            BigDecimal minBigDecimal = new BigDecimal(10000);

            // 金额大于10000 那么手续费为金额*5
            if(transactions.getAmount().compareTo(minBigDecimal) > 0){
                transactions.setCost(transactions.getAmount().multiply(new BigDecimal("0.0005")));
            }else {
                // 金额小于10000 那么手续费为5
                transactions.setCost(new BigDecimal("5"));
            }
        } else if (DealTypeEnum.SELL.status.equals(transactions.getType())) {
            // 卖出 手续费为0.001 千分之 1
            transactions.setCost(transactions.getAmount().multiply(new BigDecimal("0.001")));
        }
    }


    /**
     * 判断交易记录是否合法
     *
     * @param transactions 交易记录
     * @return 是否合法
     */
    private Boolean isExist(Transactions transactions,User user) {

        // 判断买入日期是否在工作日之内
        final Workday workday = workdayMapper.selectOne(new LambdaQueryWrapper<Workday>().eq(Workday::getDate, transactions.getDate()));
        if (workday == null) {
            GraceException.display("买入日期不在工作日之内");
            return false;
        }

        if(transactions.getAmount().add(transactions.getCost()).compareTo(user.getAmount()) > 0){
            GraceException.display("余额不足无法买入");
            return false;
        }

        // 查询数量是否是整百的
        if (transactions.getQuantity() % 100 != 0) {
            GraceException.display("交易数量必须是100的倍数");
            return false;
        }

        if (transactions.getQuantity() < 100) {
            GraceException.display("交易数量不能小于100");
            return false;
        }

        final Code code = codeMapper.selectOne(new LambdaQueryWrapper<Code>().eq(Code::getDm, transactions.getDm()));
        // 查询 股票在库中有没有
        if (code == null) {
            GraceException.display("股票代码错误");
            return false;
        }


        if (EnumUtils.existsInEnum(DealTypeEnum.class, transactions.getType(), "status")) {
            GraceException.display("交易类型错误");
            return false;
        }
        return true;
    }


}
